#include "IO.h"
#include <sstream>
#include <fstream>

#include <osgDB/ReadFile>
#include <osg/Geometry>
#include <osg/Drawable>

#ifdef _DEBUG
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#else
#pragma comment(lib, "osg.lib")
#pragma comment(lib, "osgDB.lib")
#endif


void findEveryGeode(Node* node, vector<Geode*>& geodeList)
{
	Geode* geode = dynamic_cast<Geode*>(node);
	if (geode)
	{
		int size = geodeList.size();
		for (int i = 0; i < size; ++i)
		{
			if (geodeList[i] == geode)
			{
				return;
			}
		}
		geodeList.push_back(geode);
		return;
	}

	Group* group = dynamic_cast<Group*>(node);
	if (group)
	{
		int num = group->getNumChildren();
		for (int i = 0; i < num; ++i)
		{
			findEveryGeode(group->getChild(i), geodeList);
		}
	}
}

bool isAdjacent(Geode* geode1, Geode* geode2)
{
	const BoundingBox& bound1 = geode1->getBoundingBox();
	const BoundingBox& bound2 = geode2->getBoundingBox();

	if (!bound1.intersects(bound2))
	{
		return false;
	}
	else
	{
		return true;
	}
}

void flatGeode(Geode* geode)
{
	MatrixList matrixList = geode->getWorldMatrices();
	Matrixd worldMatrix = matrixList[0];

	const Geode::DrawableList& drawList = geode->getDrawableList();
	Geode::DrawableList::const_iterator iter = drawList.begin();
	for (; iter != drawList.end(); ++iter)
	{
		Geometry* geom = dynamic_cast<Geometry*>((*iter).get());
		if (geom)
		{
			Vec3Array& vertex = static_cast<Vec3Array&>(*geom->getVertexArray());
			for (unsigned i = 0; i < vertex.size(); ++i)
			{
				Vec3d tmpVertex = worldMatrix.preMult(vertex[i]);
				vertex[i].x() = tmpVertex.x();
				vertex[i].y() = tmpVertex.y();
			}

			geom->dirtyBound();
		}
	}
}





bool readXML(const string& filename, property_tree::wptree& xml)
{
	locale oldLocale;
	locale utf8Locale(oldLocale, new program_options::detail::utf8_codecvt_facet);

	try
	{
		property_tree::xml_parser::read_xml(filename, xml, 0, utf8Locale);
	}
	catch (...)
	{
		return false;
	}

	locale::global(oldLocale);

	return true;
}

bool writeXML(const string& filename, property_tree::wptree& xml)
{
	locale oldLocale;
	locale utf8Locale(oldLocale, new program_options::detail::utf8_codecvt_facet);

	try
	{
		property_tree::xml_parser::write_xml(filename, xml, utf8Locale);
	}
	catch (...)
	{
		return false;
	}

	locale::global(oldLocale);

	return true;
}

void initAdjacencyListManully()
{
	ACTIVATED_NODE_COUNT = 10;
	INACTIVATED_NODE_COUNT = 3;

	g_adjacencyList = new NodeHead[ACTIVATED_NODE_COUNT + INACTIVATED_NODE_COUNT];

	int i = 0;
	for (; i < ACTIVATED_NODE_COUNT; ++i)
	{
		g_adjacencyList[i].id = i;
	}
	for (; i < INACTIVATED_NODE_COUNT + ACTIVATED_NODE_COUNT; ++i)
	{
		g_adjacencyList[i].id = -(i - ACTIVATED_NODE_COUNT + 1);
	}


	g_adjacencyList[ACTIVATED_NODE_COUNT + 0].colorIndex = 0;
	g_adjacencyList[ACTIVATED_NODE_COUNT + 1].colorIndex = 1;
	g_adjacencyList[ACTIVATED_NODE_COUNT + 2].colorIndex = 1;

	NodeRef* newNodeRef;
	NodeRef* lastNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[1]);
	g_adjacencyList[0].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[5]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[3]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[2]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[4]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[0]);
	g_adjacencyList[1].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[3]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[6]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[4]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[0]);
	g_adjacencyList[2].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[4]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[7]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[5]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[ACTIVATED_NODE_COUNT + 2]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;
    
	newNodeRef = new NodeRef(&g_adjacencyList[1]);
	g_adjacencyList[3].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[0]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[6]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[8]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[ACTIVATED_NODE_COUNT + 1]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[1]);
	g_adjacencyList[4].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[2]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[0]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[6]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[7]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[2]);
	g_adjacencyList[5].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[0]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[7]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[9]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[ACTIVATED_NODE_COUNT]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[3]);
	g_adjacencyList[6].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[4]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[1]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[7]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[8]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	g_adjacencyList[7].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[5]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[2]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[6]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[9]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[6]);
	g_adjacencyList[8].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[9]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[3]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[ACTIVATED_NODE_COUNT + 1]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;
    
	newNodeRef = new NodeRef(&g_adjacencyList[7]);
	g_adjacencyList[9].nodeRef = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[8]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[5]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;

	newNodeRef = new NodeRef(&g_adjacencyList[ACTIVATED_NODE_COUNT]);
	lastNodeRef->next = newNodeRef;
	lastNodeRef = newNodeRef;
	
}

void initAdjacencyListAutomatically(property_tree::wptree& configXML)
{
	map<string, int> predefined;
	vector<string> nameList;
	vector<Geode*> geodeList;

	string earthFile = wstringToString(configXML.get<wstring>(L"config.earthFile"));
	try
	{
		BOOST_FOREACH(property_tree::wptree::value_type& v, configXML.get_child(L"config.predefined"))
		{
			if (wcscmp(v.first.data(), L"colorIndex") != 0) { continue; }

			string name = wstringToString(v.second.get<wstring>(L"<xmlattr>.name"));
			int colorIndex = v.second.get_value(0);
			
			predefined[name] = colorIndex;
		}
	}
	catch(...)
	{
	}

	property_tree::wptree earthXML;
	readXML(earthFile, earthXML);
	BOOST_FOREACH(property_tree::wptree::value_type& v, earthXML.get_child(L"map"))
	{
		if (wcscmp(v.first.data(), L"model") != 0) { continue; }

		string name = wstringToString(v.second.get<wstring>(L"features.<xmlattr>.name"));
		nameList.push_back(name);
	}
	Node* node = osgDB::readNodeFile(earthFile);
	findEveryGeode(node, geodeList);
	for (unsigned int i = 0; i < geodeList.size(); ++i)
	{
		geodeList[i]->setName(nameList[i]);
		flatGeode(geodeList[i]);
	}

	g_adjacencyList = new NodeHead[geodeList.size()];
	INACTIVATED_NODE_COUNT = predefined.size();
	ACTIVATED_NODE_COUNT = geodeList.size() - INACTIVATED_NODE_COUNT;

	int i = 0, j = geodeList.size() - 1;
	for (unsigned int k = 0; k < geodeList.size(); ++k)
	{
		if (predefined.find(nameList[k]) == predefined.end())
		{
			g_adjacencyList[i].id = i;
			g_adjacencyList[i].indexInGeodeList = k;
			g_adjacencyList[i].name = nameList[k];
			++i;
		}
		else
		{
			g_adjacencyList[j].id = ACTIVATED_NODE_COUNT - j - 1;
			g_adjacencyList[j].colorIndex = predefined[nameList[k]];
			g_adjacencyList[j].indexInGeodeList = k;
			g_adjacencyList[j].name = nameList[k];
			--j;
		}
	}

	int nodeCount = geodeList.size();
	int* tested = new int[nodeCount * nodeCount];
	memset(tested, 0, sizeof(int) * nodeCount * nodeCount);

#define TESTED(i, j) tested[i * nodeCount + j]

	NodeRef* lastRef = NULL;
	for (int i = 0; i < ACTIVATED_NODE_COUNT; ++i)
	{
		Geode* geode1 = geodeList[ g_adjacencyList[i].indexInGeodeList ];
		lastRef = NULL;
		
		for (unsigned int j = 0; j < geodeList.size(); ++j)
		{
			if (i == j) { continue; }

			if (TESTED(i, j) == -1)
			{
				continue;
			}
			else if (TESTED(i, j) == 0)
			{
				Geode* geode2 = geodeList[ g_adjacencyList[j].indexInGeodeList ];
				if (!isAdjacent(geode1, geode2))
				{
					TESTED(i, j) = TESTED(j, i) = -1;
					continue;
				}
				else
				{
					TESTED(i, j) = TESTED(j, i) = 1;
				}
			}

			NodeRef* newRef = new NodeRef(&g_adjacencyList[j]);
			if (lastRef == NULL)
			{
				g_adjacencyList[i].nodeRef = newRef;
				lastRef = newRef;
			}
			else
			{
				lastRef->next = newRef;
				lastRef = newRef;
			}
		}
	}
	delete [] tested;
#undef TESTED

}

void destroyAdjacencyList()
{
	if (!g_adjacencyList)
	{
		return;
	}

	for (int i = 0; i < ACTIVATED_NODE_COUNT; ++i)
	{
		NodeRef* p1 = g_adjacencyList[i].nodeRef;
		NodeRef* p2 = p1;

		while (p1)
		{
			p2 = p1->next;
			delete p1;
			p1 = p2;
		}
	}

	delete [] g_adjacencyList;
}

void initColorsManully()
{
	COLOR_COUNT = 4;
	g_colors = new wstring[COLOR_COUNT];
	for (int i = 0; i < COLOR_COUNT; ++i)
	{
		g_colors[i] = i;
	}
}

void initColorsAutomatically(property_tree::wptree& configXML)
{
	property_tree::wptree colors = configXML.get_child(L"config.colors");

	COLOR_COUNT = colors.size();
	g_colors = new wstring[COLOR_COUNT];

	int i = 0;

	BOOST_FOREACH(property_tree::wptree::value_type& v, colors)
	{
		if (wcscmp(v.first.data(), L"color") != 0) { continue; }
		g_colors[i++] = v.second.get_value(L"0");
	}
}

void destroyColors()
{
	if (!g_colors)
	{
		return;
	}
	delete [] g_colors;
}


